package myapps.ttclient.app;

import java.io.File;
import java.io.IOException;
import java.util.HashSet;

import myapps.ttclient.GlobalVariables;
import myapps.ttclient.R;
import myapps.ttclient.asymmetric_encryption.RSAKey;
import myapps.ttclient.asymmetric_encryption.RSAKeyManager;
import myapps.ttclient.baseclass_and_interface.TTConnectionStateChangedListener;
import myapps.ttclient.baseclass_and_interface.TTClientConnection;
import myapps.ttclient.baseclass_and_interface.TTClientMessage;
import myapps.ttclient.baseclass_and_interface.TTClientMessageReceiver;
import myapps.ttclient.connection.Connection;
import myapps.ttclient.connection.ConnectionListManager;
import myapps.ttclient.protocol.message.AuthorityResetMesssage;
import myapps.ttclient.protocol.message.NoticeMessage;
import myapps.ttclient.protocol.message.RSAPublicKeyClientMessage;
import myapps.ttclient.protocol.message.RSAPublicKeyServerMessage;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Handler;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.widget.Toast;

public class TTClientApplication extends Application implements Runnable {
	//////////////////////////////////////////////////////////////////////////
	private static final long CONNECTION_CLOSE_DELAY = 100;

	private SharedPreferences preferences;

	private Vibrator vibrator;

	private HashSet<TTClientMessageReceiver> messageReceivers;

	private HashSet<TTConnectionStateChangedListener> connectionStateChangedListeners;

	private Handler handler;

	private TTClientConnection[] connection;

	private CloseConnectionScheduler closeConnectionScheduler;

	private ConnectionListManager connectionListManager;

	private boolean requestEnableBluetooth;

	private boolean bFirstRun = true;

	@Override
	public void onCreate() {
		super.onCreate();

		this.preferences = PreferenceManager.getDefaultSharedPreferences(this);

		this.vibrator = (Vibrator) this.getSystemService(Context.VIBRATOR_SERVICE);
			
		this.messageReceivers = new HashSet<TTClientMessageReceiver>();

		this.connectionStateChangedListeners = 
			new HashSet<TTConnectionStateChangedListener>();

		this.handler = new Handler();

		this.connection = new TTClientConnection[1];

		this.closeConnectionScheduler = new CloseConnectionScheduler();

		this.connectionListManager = new ConnectionListManager(this.preferences);

		this.requestEnableBluetooth = true;

	}

	public SharedPreferences getPreferences() {
		return this.preferences;
	}

	public ConnectionListManager getConnectionListManager() {
		return this.connectionListManager;
	}

	public void vibrate(long l) {
		if (this.preferences.getBoolean("feedback_vibration", true)) {
			this.vibrator.vibrate(l);
		}
	}

	public TTClientConnection getConnection() {
		return this.connection[0];
	}

	public boolean requestEnableBluetooth() {
		boolean b = this.requestEnableBluetooth;

		this.requestEnableBluetooth = false;

		return b;
	}

	public synchronized void run() {
		if(bFirstRun == true) {
			RSAKeyManager.getInstance();
			bFirstRun = false;
		}

		Connection co = this.connectionListManager.getUsed();

		if (co != null) {
			TTClientConnection c = null;

			try {
				c = co.connect(this);

				synchronized (this.connection) {
					this.connection[0] = c;
					onConnectionStateChanged() ;
				}

				try {
					this.showToast("Connection established");

					while (true) {
						TTClientMessage msg = c.receiveMessage();

						this.receiveMessage(msg);
					}
				} finally {
					synchronized (this.connection) {
						this.connection[0] = null;
						
						GlobalVariables.setCurrrentAuthority(GlobalVariables.AUTHORITY_GUEST);
						GlobalVariables.setTemporaryKey("");
						
						onConnectionStateChanged() ;
					}

					c.close();
				}
			} catch (IOException e)	{
				if (c == null) {
					this.showToast("Connection refused");
				} else {
					this.showToast("Connection closed");
				}
			} catch (IllegalArgumentException e) {
				this.showToast("Illegal connection parameter");
			}
		} else {
			this.showToast("No connection selected");
		}
	}

	public void sendMessage(TTClientMessage message) {
		synchronized (this.connection) {
			if (this.connection[0] != null) {
				try {
					this.connection[0].sendMessage(message);
				} catch (IOException e) {
					
				}
			}
		}
	}

	public void showInternalToast(int resId) {
		if (this.isInternalToast()) {
			this.showToast(resId);
		}
	}

	public void showInternalToast(String message) {
		if (this.isInternalToast()) {
			this.showToast(message);
		}
	}

	public boolean isInternalToast() {
		synchronized (this.messageReceivers) {
			return !this.messageReceivers.isEmpty();
		}
	}

	public void showToast(int resId) {
		this.showToast(this.getResources().getString(resId));
	}

	public void showToast(final String message) {
		this.handler.post(new Runnable() {
			public void run() {
				Toast.makeText(TTClientApplication.this, message, Toast.LENGTH_SHORT).show();
			}
		});
	}

	public void postToQueue(Runnable r) {
		this.handler.post(r);
	}

	private void receiveMessage(TTClientMessage message) {
		synchronized (this.messageReceivers) {
			for (TTClientMessageReceiver messageReceiver : this.messageReceivers) {
				messageReceiver.receiveMessage(message);
			}

			if(message instanceof AuthorityResetMesssage) {
				GlobalVariables.setCurrrentAuthority(GlobalVariables.AUTHORITY_GUEST);
				GlobalVariables.setTemporaryKey("");
			}

			if(message instanceof NoticeMessage) {
				final String str = ((NoticeMessage) message).getNotice();

				if(str != null) {
					this.showToast(str);
				}
			}

			if(message instanceof RSAPublicKeyServerMessage) {
				RSAPublicKeyServerMessage msg = (RSAPublicKeyServerMessage) message;

				RSAKey serverPublicKey = new RSAKey(msg.modulus, msg.exponent);

				RSAKeyManager.getInstance().set_serverPublicKey(serverPublicKey);
			}
		}
	}

	public void connect() {
		synchronized (this.connection) {
			if (this.connection[0] == null) {
				(new Thread(this)).start();
			}
		}
	}

	public void registerMessageReceiver(TTClientMessageReceiver messageReceiver) {
		synchronized (this.messageReceivers) {
			this.messageReceivers.add(messageReceiver);

			//			if (this.messageReceivers.size() > 0)
			//			{
			//				synchronized (this.connection)
			//				{
			//					if (this.connection[0] == null)
			//					{
			//						(new Thread(this)).start();
			//					}
			//				}
			//			}
		}
	}

	public void unregisterMessageReceiver(TTClientMessageReceiver messageReceiver) {
		synchronized (this.messageReceivers) {
			this.messageReceivers.remove(messageReceiver);

			//if (this.messageReceivers.size() == 0)
			//{
			//	this.closeConnectionScheduler.schedule();
			//}
		}
	}

	public void disconnect() {
		this.closeConnectionScheduler.schedule();
	}

	public void registerConnectionStateChangedListener(
			TTConnectionStateChangedListener listener) {
		synchronized (this.connectionStateChangedListeners)
		{
			this.connectionStateChangedListeners.add(listener);
		}
	}

	public void onConnectionStateChanged() {

		TTClientConnection clientConnection = this.getConnection();
		if(clientConnection != null) {
			RSAKeyManager mng = RSAKeyManager.getInstance();
			RSAKey clientPublicKey = mng.get_clientPublicKey();

			byte[] bytearray_modulus = clientPublicKey.get_modulus().toByteArray();
			byte[] bytearray_exponent = clientPublicKey.get_exponent().toByteArray();

			if (bytearray_modulus[0] == 0) {
				byte[] tmp = new byte[bytearray_modulus.length - 1];
				System.arraycopy(bytearray_modulus, 1, tmp, 0, tmp.length);
				bytearray_modulus = tmp;
			}

			if (bytearray_exponent[0] == 0) {
				byte[] tmp = new byte[bytearray_exponent.length - 1];
				System.arraycopy(bytearray_exponent, 1, tmp, 0, tmp.length);
				bytearray_exponent = tmp;
			}

			this.sendMessage(new RSAPublicKeyClientMessage(
					bytearray_modulus, bytearray_exponent));
			
		}
		
		this.postToQueue(new Runnable() {

			@Override
			public void run() {
				synchronized (TTClientApplication.this.connectionStateChangedListeners) {
					for (TTConnectionStateChangedListener listener : 
						TTClientApplication.this.connectionStateChangedListeners) {
						listener.onConnectionStateChanged();
					}
				}

			}
		});
	}

	public void unregisterConnectionStateChangedListener(
			TTConnectionStateChangedListener listener) {
		synchronized (this.connectionStateChangedListeners) {
			this.connectionStateChangedListeners.remove(listener);
		}
	}
	
	public void removeAllConnectionSettings() {
		this.connectionListManager.RemoveAll();
		this.connectionListManager.Save();
	}
	
	
	public void clearApplicationData() {
		File cache = getCacheDir();
		File appDir = new File(cache.getParent());
		if (appDir.exists()) {
			String[] children = appDir.list();
			for (String s : children) {
				if (!s.equals("lib")) {
					deleteDir(new File(appDir, s));
				}
			}
		}
	}

	public static boolean deleteDir(File dir) {
		if (dir != null && dir.isDirectory()) {
			String[] children = dir.list();
			for (int i = 0; i < children.length; i++) {
				boolean success = deleteDir(new File(dir, children[i]));
				if (!success) {
					return false;
				}
			}
		}

		return dir.delete();
	}

	private class CloseConnectionScheduler implements Runnable {
		private Thread currentThread;

		public synchronized void run() {
			try {
				//				//this.wait(TTClientApplication.CONNECTION_CLOSE_DELAY);
				//				
				//				synchronized (TTClientApplication.this.messageReceivers)
				//				{
				//					if (TTClientApplication.this.messageReceivers.size() == 0)
				//					{
				//						synchronized (TTClientApplication.this.connection)
				//						{
				//							if (TTClientApplication.this.connection[0] != null)
				//							{
				//								TTClientApplication.this.connection[0].close();
				//								
				//								TTClientApplication.this.connection[0] = null;
				//							}
				//						}
				//					}
				//				}
				//				
				//				this.currentThread = null;
				////////////////////////////////////////////////////////////////////////
				if (TTClientApplication.this.connection[0] != null) {
					TTClientApplication.this.connection[0].close();

					TTClientApplication.this.connection[0] = null;
				}
				
				this.currentThread = null;
			}
			//			catch (InterruptedException e)
			//			{
			//			}
			catch (IOException e){
			}
		}

		public synchronized void schedule() {
			if (this.currentThread != null) {
				this.currentThread.interrupt();
			}

			this.currentThread = new Thread(this);

			this.currentThread.start();
		}
	}
}
